﻿using System;
using log4net;
using System.Data;
using System.IO;
using Base.Core;
using WpfAi.Enum;
using WpfAi.Core;
using System.Linq;
using WpfAi.Model;
using Brilliants.UI;
using System.Windows;
using System.Drawing;
using WpfAi.Protocol;
using System.IO.Ports;
using WpfAi.TestModule;
using System.Threading;
using System.Windows.Input;
using System.Threading.Tasks;
using System.Drawing.Imaging;
using Brilliants.UI.ControlsEx;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime.InteropServices;
using System.Text;

namespace WpfAi.Vm
{
    public class MainWindowVm : VmBase
    {
        #region ___类内变量___
        static int bmpMapBuffer = 640;
        static int bmpMapWidth = bmpMapBuffer / 2;
        static int bmpMapHeight = 240;
        AiTypeFinder p = new AiTypeFinder();
        private Thread threadTaskJob;
        static byte[] fullPicBuffer = new byte[bmpMapBuffer * bmpMapHeight];
        string CoreQRCode = string.Empty;
        string ScreenQRCode = string.Empty;
        #endregion

        #region ___构造函数___
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="windowType"></param>
        public MainWindowVm(EnumWindowType windowType, EnumBoardType boardType)
        {
            GlobalEnvironment.WindowType = windowType;
            GlobalEnvironment.BoardType = boardType;
            GlobalEnvironment.Serial = new SerialClass();
            GlobalEnvironment.Serial.DataReceived += new SerialClass.SerialPortDataReceiveEventArgs(sc_DataReceived);
            SingletonProvider<Analysis>.Instance.OnPlate += Instance_OnPlate;
            InitSerials();
            InitRate();
            InitModule();
        }

        #endregion

        #region ___绑定属性___
        protected ObservableCollection<TestResult> m_ListTestModule;
        /// <summary>
        /// 升级方式
        /// </summary>s
        public ObservableCollection<TestResult> ListTestModule
        {
            get { return m_ListTestModule; }
            set { m_ListTestModule = value; OnPropertyChanged(nameof(ListTestModule)); }
        }

        protected ObservableCollection<string> m_ListSerials = new ObservableCollection<string>();
        /// <summary>
        /// 串口
        /// </summary>s
        public ObservableCollection<string> ListSerials
        {
            get { return m_ListSerials; }
            set { m_ListSerials = value; OnPropertyChanged(nameof(ListSerials)); }
        }
        protected string m_SelectSerials = string.Empty;
        /// <summary>
        /// 串口
        /// </summary>s
        public string SelectedSerial
        {
            get { return m_SelectSerials; }
            set { m_SelectSerials = value; OnPropertyChanged(nameof(SelectedSerial)); }
        }

        protected ObservableCollection<int> m_ListRate = new ObservableCollection<int>() { 1500000 };
        /// <summary>
        /// 波特率
        /// </summary>s
        public ObservableCollection<int> ListRate
        {
            get { return m_ListRate; }
            set { m_ListRate = value; OnPropertyChanged(nameof(ListRate)); }
        }
        protected int m_SelectRate = GlobalEnvironment.Rate = 1000000;
        /// <summary>
        /// 串口
        /// </summary>s
        public int SelectedRate
        {
            get { return m_SelectRate; }
            set
            {
                m_SelectRate = value;
                GlobalEnvironment.Rate = m_SelectRate;
                OnPropertyChanged(nameof(SelectedRate));
            }
        }

        protected bool m_SerialOpen = false;
        /// <summary>
        /// 串口
        /// </summary>s
        public bool SerialOpen
        {
            get { return m_SerialOpen; }
            set { m_SerialOpen = value; OnPropertyChanged(nameof(SerialOpen)); }
        }
        protected bool m_CanStart = true;
        /// <summary>
        /// 是否可以开始测试
        /// </summary>s
        public bool CanStart
        {
            get { return m_CanStart; }
            set { m_CanStart = value; OnPropertyChanged(nameof(CanStart)); }
        }
        protected bool m_OnLine = false;
        /// <summary>
        /// 串口
        /// </summary>s
        public bool OnLine
        {
            get { return m_OnLine; }
            set { m_OnLine = value; OnPropertyChanged(nameof(OnLine)); }
        }
        protected int m_FailTotal = 0;
        /// <summary>
        /// 失败数量
        /// </summary>s
        public int FailTotal
        {
            get { return m_FailTotal; }
            set { m_FailTotal = value; OnPropertyChanged(nameof(FailTotal)); }
        }
        protected int m_SuccessTotal = 0;
        /// <summary>
        /// 通过数量
        /// </summary>s
        public int SuccessTotal
        {
            get { return m_SuccessTotal; }
            set { m_SuccessTotal = value; OnPropertyChanged(nameof(SuccessTotal)); }
        }
        protected int m_NormalTotal = 0;
        /// <summary>
        /// NG数量
        /// </summary>s
        public int NormalTotal
        {
            get { return m_NormalTotal; }
            set { m_NormalTotal = value; OnPropertyChanged(nameof(NormalTotal)); }
        }
        /// <summary>
        /// 图片
        /// </summary>
        private string m_CameraPicPath;

        public string CameraPicPath
        {
            get { return m_CameraPicPath; }
            set { m_CameraPicPath = value; OnPropertyChanged(nameof(CameraPicPath)); }
        }

        private bool m_CheckAll = true;
        public bool CheckAll
        {
            get { return m_CheckAll; }
            set { m_CheckAll = value; OnPropertyChanged(nameof(CheckAll)); }
        }

        private EnumState m_TestState;
        public EnumState TestState
        {
            get { return m_TestState; }
            set { m_TestState = value; OnPropertyChanged(nameof(TestState)); }
        }
        #endregion

        #region ___命令___
        /// <summary>
        /// 首次显示
        /// </summary>
        public ICommand LoadedCommand => new DelegateCommand(o =>
        {
            ShowQRCode();
        });
        /// <summary>
        /// 窗口关闭
        /// </summary>
        public ICommand UnLoadedCommand => new DelegateCommand(o =>
        {

        });
        /// <summary>
        /// 显示扫码窗口
        /// </summary>
        public ICommand ShowQRCodeCommand => new DelegateCommand(o =>
        {
            this.ShowQRCode();
        });
        /// <summary>
        /// 开始测试
        /// </summary>
        /// <param name=""></param>
        /// <returns></returns>
        public ICommand StartTestCommand => new DelegateCommand(o =>
        {
            StartTestCase();
        });
        /// <summary>
        /// 打开串口
        /// </summary>
        public ICommand OpenSerialCommand => new DelegateCommand(o =>
        {
            OpenOrCloseSerial();
        });
        /// <summary>
        /// 全选/不选
        /// </summary>
        public ICommand CheckAllCommand => new DelegateCommand(o =>
        {
            CheckAll = !CheckAll;
            ListTestModule.ToList().ForEach(t => t.Checked = CheckAll);
        });
        #endregion

        #region ___类内方法___
        /// <summary>
        /// 显示二维码窗口
        /// </summary>
        private void ShowQRCode()
        {
            Application.Current.Dispatcher.Invoke(() =>
            {
                QRCodeWindow qRCodeWindow = new QRCodeWindow(GlobalEnvironment.WindowType, GlobalEnvironment.BoardType);
                qRCodeWindow.DataContext = new QRCodeWindowVm(GlobalEnvironment.WindowType, GlobalEnvironment.BoardType);
                if (qRCodeWindow.ShowDialog() == true)
                {
                    var dataContext = qRCodeWindow.DataContext as QRCodeWindowVm;
                    this.CoreQRCode = dataContext.PCBQRCode;
                    this.ScreenQRCode = dataContext.ScreenQRCode;
                    StartTestCase();
                }
            });
        }
        /// <summary>
        /// 开始测试
        /// </summary>
        private void StartTestCase()
        {
            InitTestCase();
            /////////////////开始测试////////////////
            Application.Current.Dispatcher.BeginInvoke(new Action(() =>
            {
                if (!OpenSerial(true))
                    return;
                Thread.Sleep(100);
                System.Threading.Tasks.Task.Factory.StartNew(() =>
                {
                    /////////////////没有测试项////////////////
                    var testCase = ListTestModule.Where(t => t.Checked);
                    if (testCase.Count() == 0)
                        return;
                    /////////////////先查询状态////////////////
                    var protocol = new SearchStatus();
                    var sData = protocol.GetByte();
                    GlobalEnvironment.Serial.SendData(sData, 0, sData.Length);
                    Thread.Sleep(700);
                    if (!CanStart)
                        return;
                    CanStart = false;
                    /////////////////发复位命令////////////////
                    var resetProtocol = new Reset();
                    sData = resetProtocol.GetByte();
                    GlobalEnvironment.Serial.SendData(sData, 0, sData.Length);
                    Thread.Sleep(700);
                    /////////////////开始测试////////////////
                    this.TestState = EnumState.Running;
                    DateTime logTime = DateTime.Now;
                    WriteTestLog(logTime, GlobalEnvironment.GetStationName() + GlobalEnvironment.PCBQRCode + " StartTest");
                    foreach (var t in testCase)
                    {
                        t.StartTime = DateTime.Now;
                        t.State = EnumState.Running;
                        t.Clazz.StartTest((r) =>
                        {
                            if (null == r)
                            {
                                t.State = Enum.EnumState.Fail;
                                t.Message = "用户操作超时";
                                t.Result = false;
                            }
                            else
                            {
                                t.State = (r.Success ? Enum.EnumState.Success : Enum.EnumState.Fail);
                                t.Message = r.Message;
                                t.Break = r.Break;
                                t.Result = true;
                            }
                            t.EndTime = DateTime.Now;
                            WriteTestLog(
                                logTime,
                                " TestCase: " + t.Title + " " +
                                " StartTime: " + t.StartTime.ToString("yyyy-MM-dd HH:mm:ss") + " " +
                                " EndTime: " + t.EndTime.ToString("yyyy-MM-dd HH:mm:ss") + " " +
                                " Result: " + (r.Success ? "Pass" : "Fail") +
                                " Time(ms): " + (t.EndTime - t.StartTime).TotalMilliseconds + " " +
                                " Message: " + t.Message
                                );
                            //////////失败则终止////////
                            if (!r.Success || r == null)
                                t.Break = true;
                        });
                        if (t.Break)
                            break;
                    }
                    Thread.Sleep(100);
                    CanStart = true;
                    this.SuccessTotal = testCase.Where(t => t.State == EnumState.Success).Count();
                    this.FailTotal = testCase.Where(t => t.State == EnumState.Fail).Count();
                    this.NormalTotal = testCase.Where(t => t.State == EnumState.Normal).Count();
                    this.TestState = this.FailTotal == 0 ? EnumState.Success : EnumState.Fail;
                    //////////写入SN号码///////
                    if (this.TestState == EnumState.Success
                    && GlobalEnvironment.WindowType == EnumWindowType.Core
                    && GlobalEnvironment.BoardType == EnumBoardType.Board)
                        WriteSN();
                    //////////保存操作日志///////
                    SaveCrv(logTime, testCase.ToList());
                    var result = 0;
                    UpLoadTestResult(out result);
                    Thread.Sleep(100);
                    CloseSerial();
                    ShowQRCode();
                });
            }));
        }
        /// <summary>
        /// 上传测试结果
        /// </summary>
        private void UpLoadTestResult(out int result)
        {
            var requestCode = GlobalEnvironment.WindowType == EnumWindowType.Core ? GlobalEnvironment.ScreenQRCode : GlobalEnvironment.PCBQRCode;
            Base.Core.MesInterface.UploadResult(requestCode,
                this.TestState == EnumState.Success ? "1" : "2",
                this.TestState == EnumState.Success ? "0" : "1", GlobalEnvironment.GetStationID(), out result);
        }
        /// <summary>
        /// 初始化界面
        /// </summary>
        private void InitTestCase()
        {
            Application.Current.Dispatcher.BeginInvoke(new Action(() =>
            {
                Task.Run(() =>
                {
                    this.SuccessTotal = 0;
                    this.FailTotal = 0;
                    this.NormalTotal = 0;
                    ListTestModule.ToList().ForEach((t) =>
                    {
                        t.State = EnumState.Normal;
                        t.Message = string.Empty;
                        t.Duration = 0;
                    });
                    this.TestState = EnumState.Normal;
                });
            }));
        }
        /// <summary>
        /// 初始化串口
        /// </summary>
        private void InitSerials()
        {
            Task.Factory.StartNew(() =>
            {
                while (true)
                {
                    var serials = GlobalEnvironment.Serial.getSerials();
                    var listComs = serials.ToList().Where(t => !t.Equals("COM1")).Distinct();
                    ListSerials = new ObservableCollection<string>(listComs);
                    if (ListSerials.Count == 0 || !ListSerials.Contains(this.SelectedSerial))
                    {
                        this.SerialOpen = false;
                    }
                    this.SelectedSerial = ListSerials.Count > 0 ? ListSerials[0] : null;
                    GlobalEnvironment.Com = this.SelectedSerial;
                    Thread.Sleep(100);
                }
            });
        }
        /// <summary>
        /// 初始化波特率
        /// </summary>
        private void InitRate()
        {
            ListRate = new ObservableCollection<int>()
            {
                1000000,
                1500000,
            };
        }
        /// <summary>
        /// 初始化测试模块
        /// </summary>
        private void InitModule()
        {
            List<TestResult> l = new List<TestResult>();
            var modules = p.FindClassesOfType<ITestModule>().ToList();

            foreach (var t in modules)
            {
                var clazz = (ITestModule)Activator.CreateInstance(t);
                clazz.Serial = GlobalEnvironment.Serial;
                TestResult testResult = new TestResult();
                testResult.Order = clazz.Order;
                testResult.Title = clazz.Title;
                testResult.Clazz = clazz;
                if (clazz.WindowType == GlobalEnvironment.WindowType
                    || (clazz.WindowType == EnumWindowType.All && GlobalEnvironment.WindowType != EnumWindowType.Assemble))
                    l.Add(testResult);
            }
            int i = 1;
            var testCase = l.OrderBy(t => t.Order).ToList();
            testCase.ForEach((t) =>
            {
                t.Order = i++;
            });
            m_ListTestModule = new ObservableCollection<TestResult>(testCase);
        }
        /// <summary>
        /// 打开/关闭串口
        /// </summary>
        private void OpenOrCloseSerial(bool sleep = false)
        {
            try
            {
                if (SerialOpen)
                    CloseSerial();
                else
                    OpenSerial(sleep);
            }
            catch (Exception e)
            {
                NbMessageBox.Notity("串口操作失败");
            }
        }
        /// <summary>
        /// 打开串口
        /// </summary>
        private bool OpenSerial(bool sleep = false)
        {
            bool r = false;
            try
            {
                GlobalEnvironment.Serial.closePort();
                GlobalEnvironment.Serial.setSerialPort(this.SelectedSerial, System.Convert.ToInt32(this.SelectedRate), 8, 1);
                r = GlobalEnvironment.Serial.openPort();
                StartHeartBeat();
                SerialOpen = true;
                if (sleep)
                    Thread.Sleep(500);
            }
            catch (Exception e)
            {
                NbMessageBox.Notity("打开串口失败", 0, "确定");
            }
            return r;
        }
        /// <summary>
        /// 关闭串口
        /// </summary>
        private void CloseSerial()
        {
            try
            {
                if (SerialOpen)
                {
                    GlobalEnvironment.Serial.closePort();
                    OnLine = false;
                    StopHeartBeat();
                    SerialOpen = false;
                }
            }
            catch (Exception e)
            {
                NbMessageBox.Notity("关闭串口失败");
            }
        }
        /// <summary>
        /// 发心跳
        /// </summary>
        private void StartHeartBeat()
        {
            ThreadHelper.StartBgThread(ref threadTaskJob, new ParameterizedThreadStart((t) =>
            {
                //while (true)
                {
                    var protocol = new HeartBeat();
                    var sData = protocol.GetByte();
                    GlobalEnvironment.Serial.SendData(sData, 0, sData.Length);
                    //Thread.Sleep(1000);
                }
            }), null, true);
        }
        /// <summary>
        /// 停止心跳
        /// </summary>
        private void StopHeartBeat()
        {
            threadTaskJob.Abort();
            threadTaskJob.Join();
        }
        /// <summary>
        /// 写入SN
        /// </summary>
        private void WriteSN()
        {
            if (!string.IsNullOrEmpty(GlobalEnvironment.PCBQRCode))
            {
                var protocol = new WriteSn();
                var sData = protocol.GetByte(Encoding.UTF8.GetBytes(GlobalEnvironment.PCBQRCode).ToList());
                GlobalEnvironment.Serial.SendData(sData, 0, sData.Length);
            }
        }
        /// <summary>
        /// 数据到达
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <param name="bits"></param>
        private void sc_DataReceived(object sender, SerialDataReceivedEventArgs e, byte[] bits)
        {
            SingletonProvider<Analysis>.Instance.Enqueue(bits);
        }
        /// <summary>
        /// 下位机上报事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Instance_OnPlate(object sender, ResponseWarp e)
        {
            if (e.Command == StaticCommand.U_HeartBeat)
            {
                OnLine = true;
            }
            else if (e.Command == StaticCommand.U_WriteSn)
            {
                if (e.Success)
                {
                    var protocol = new ReadSn();
                    var sData = protocol.GetByte();
                    GlobalEnvironment.Serial.SendData(sData, 0, sData.Length);
                    LogHelper.Log.Info("写入SN【" + GlobalEnvironment.PCBQRCode + "】成功");
                }
                else
                    LogHelper.Log.Info("写入SN【" + GlobalEnvironment.PCBQRCode + "】失败");
            }
            //else if (e.Command == StaticCommand.U_ReadSn)
            //{
            //    var sn = Encoding.UTF8.GetString(e.DataBuffer).Substring(0, 14);
            //}
        }
        /// <summary>
        /// 获取BMP图片
        /// </summary>
        /// <param name="w"></param>
        /// <param name="h"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public string GetBmpPicture(int w, int h, byte[] data)
        {
            Bitmap bmp = new Bitmap(w, h, PixelFormat.Format16bppRgb565);
            Rectangle rect = new Rectangle(0, 0, w, h);
            BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format16bppRgb565);
            IntPtr ptrToFirstPixel = bmpData.Scan0;
            Marshal.Copy(fullPicBuffer, 0, ptrToFirstPixel, fullPicBuffer.Length);
            bmp.UnlockBits(bmpData);
            var fileName = DateTime.Now.ToString("yyyyMMddHHmmss") + ".bmp";
            var fullPath = Path.Combine(GlobalEnvironment.PhotoStorageUrl, fileName);
            bmp.Save(fullPath, System.Drawing.Imaging.ImageFormat.Bmp);
            return fileName;
        }
        /// <summary>
        /// 创建TXT日志
        /// </summary>
        public void WriteTestLog(DateTime startTime, string messag)
        {
            var fileName = GlobalEnvironment.PCBQRCode + " " + startTime.ToString("yyyy-MM-dd HHmmss");
            var logFile = Path.Combine(CreateLogPath(startTime), fileName) + ".log";
            using (var stream = new FileStream(logFile, FileMode.OpenOrCreate, FileAccess.Write))
            {
                var writer = new StreamWriter(stream);
                writer.BaseStream.Seek(0L, SeekOrigin.End);
                writer.WriteLine("[" + DateTime.Now.ToString("yyyyMMdd HH:mm:ss.sss") + "] " + messag);
                writer.WriteLine("[" + DateTime.Now.ToString("yyyyMMdd HH:mm:ss.sss") + "] --------------------------------------------------------------------------------------------------------------------------------------------------------");
                writer.Flush();
                writer.Close();
                stream.Close();
            }
        }
        /// <summary>
        /// 创建CRV日志
        /// </summary>
        /// <param name="startTime"></param>
        public void SaveCrv(DateTime startTime, List<TestResult> testCase)
        {
            var fileName = GlobalEnvironment.PCBQRCode + " " + startTime.ToString("yyyy-MM-dd HHmmss");
            var csvFile = Path.Combine(CreateLogPath(startTime), fileName) + ".csv";
            DataTable dataTable = new DataTable();
            dataTable.Columns.Add("StationID");
            dataTable.Columns.Add("StationName");
            dataTable.Columns.Add("SerialNumber");
            dataTable.Columns.Add("TaktTime");
            dataTable.Columns.Add("TestSite");
            dataTable.Columns.Add("OperatorID");
            dataTable.Columns.Add("ProjectName");
            dataTable.Columns.Add("TestSWRev");
            dataTable.Columns.Add("TestDateTime");
            dataTable.Columns.Add("PCName");
            dataTable.Columns.Add("TestStatus");

            DataRow dr = dataTable.NewRow();
            dr["StationID"] = GlobalEnvironment.GetStationID();
            dr["StationName"] = GlobalEnvironment.GetStationName();
            dr["SerialNumber"] = GlobalEnvironment.PCBQRCode;
            dr["TaktTime"] = (DateTime.Now - startTime).TotalSeconds;
            dr["TestSite"] = "Kelion";
            dr["OperatorID"] = "Guanggan";
            dr["ProjectName"] = "WM004";
            dr["TestSWRev"] = "1.0.0.0";
            dr["TestDateTime"] = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss");
            dr["PCName"] = GlobalEnvironment.GetPCName();
            dr["TestStatus"] = this.TestState == EnumState.Success ? "Pass" : "Fail";
            dataTable.Rows.Add(dr);

            DataTable dataTableItem = new DataTable();
            dataTableItem.Columns.Add("TestDescription");
            dataTableItem.Columns.Add("TestItemName");
            dataTableItem.Columns.Add("Unit");
            dataTableItem.Columns.Add("USL");
            dataTableItem.Columns.Add("LSL");
            dataTableItem.Columns.Add("ResultValue");
            dataTableItem.Columns.Add("Status");
            dataTableItem.Columns.Add("Detail");

            testCase.ForEach(t =>
            {
                DataRow drItem = dataTableItem.NewRow();
                drItem["TestDescription"] = string.Empty;
                drItem["TestItemName"] = t.Title;
                drItem["Unit"] = string.Empty;
                drItem["USL"] = "0";
                drItem["LSL"] = "0";
                drItem["ResultValue"] = t.Result ? "0" : "1";
                drItem["Status"] = t.State == EnumState.Success ? "Pass" : "Fail";
                drItem["Detail"] = t.Message;
                dataTableItem.Rows.Add(drItem);
            });
            CSVFileHelper.SaveCSV(dataTable, dataTableItem, csvFile, GlobalEnvironment.GetStationName());
        }
        /// <summary>
        /// 创建日志目录
        /// </summary>
        /// <param name="startTime"></param>
        /// <returns></returns>
        private string CreateLogPath(DateTime startTime)
        {
            var path = Path.Combine(
                AppDomain.CurrentDomain.BaseDirectory,
                "TestReport",
                GlobalEnvironment.GetStationID() + " " + GlobalEnvironment.GetStationName(),
                startTime.ToString("yyyy-MM-dd")
                );
            if (!Directory.Exists(path))
                Directory.CreateDirectory(path);

            return path;
        }
        #endregion
    }
}